#include<stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdint.h>
#include "myalloc.h"
#include "algoritmos_memoria.h"
/*
 filename:firstfit.c
 description:implementaciòn de particiones dinamicas
 Author:Martin Zavatto
 */


void updateBLockTime(struct memory* block, int32_t criterio){
	if(criterio == FIFO){

	}
	else{
		block->lastAccsess = time(NULL);
	}
}

void store(struct memory *current, struct memory *start,int32_t minBlock){
		int32_t datasz = current->datasz;
		struct memory* block = (struct memory*) myalloc(sizeof(struct memory));
		//si el dato es menor que el tamaño minimo de bloque, le asigno el tamaño minimo.
		if(datasz<minBlock)
			datasz=minBlock;
		if(current->bsz - datasz > 0){
			block->stored = 0;
			block->bsz = current->bsz - datasz;
			current->bsz = datasz;
			block->data = current->data + current->bsz;
			block->link = current->link;
			block->lastAccsess = time(NULL);
			current->link = block;
		}
}



struct memory* find_prev(struct memory* current,struct memory *start){
	struct  memory* aux = start;
	struct memory* prev = NULL;
	while(aux != NULL){
		if(aux == current){
			break;
		}
		prev = aux;
		aux = aux->link;
	}
	return prev;
}

struct memory* best_fit(int32_t datasz,struct memory *start){
    struct memory* bestBlock = NULL;
    struct memory* current = start;
    int32_t min=0;
    int32_t aux=0;
    int32_t flag=0;

    while (current != NULL) {
        if (current->stored == 0) {
            aux = current->bsz - datasz;
            if(aux >=0 && flag == 0){
            	min = aux;
            	flag=1;
            }

            if(aux >=0 && aux <= min){
                min = aux;
                bestBlock = current;
            }
        }
        current = current->link;
    }
    if (bestBlock == NULL){
        printf("No se enconcontro espacio disponible, usando Best fit\n");
    }else{
        printf("Se enconcontro espacio de %d en la posicion %p, usando Best fit\n", datasz, bestBlock);
    }
    return bestBlock;
}

struct memory* first_fit(int32_t datasz,struct memory *start) {
	 struct memory* current = start;
	/*busco el primer bloque de memoria suficientemente grande*/
	while (current != NULL) {
		if (current->stored == 0 && current->bsz >= datasz) {
			break;
		}
		current = current->link;
	}
	if (current == NULL)
		 printf("No se enconcontro espacio disponible, usando First fit\n");

	return current;
}

struct memory* allocate(int32_t datasz, struct memory *start, int32_t criterio){
	switch(criterio){
	case FIRST_FIT:
		return first_fit(datasz,start);
	case BEST_FIT:
		return best_fit(datasz,start);
	}
	return NULL;
}

void compactar(struct memory *start){
	struct memory* current = start;
		int32_t bsz_aux;
		int32_t compactado = 0;

		while(current != NULL && current->link != NULL){
			compactado=0;
			if(current->stored == 0 && current->link->stored==1){
				//me guardo el size del bloque libre
				bsz_aux = current->bsz;
				//muevo el los datos del bloque actual al bloque libre anterior
				memcpy(current->data,current->link->data,current->link->datasz);
				current->datasz = current->link->datasz;
				current->keysz = current->link->keysz;
				strcpy(current->key,current->link->key);
				current->bsz = current->link->bsz;
				//lo marco como ocupado
				current->stored = 1;
				//el bloque actual ahora tiene el tamaño que tenia el libre
				current->link->bsz = bsz_aux;
				current->link->data= current->data + current->datasz;
				current->link->stored = 0;
				//vuelvo al principio
				compactado = 1;
				current = start;
			}
			//si hay dos libres consecutivos los junto
			if(current->stored == 0 && current->link->stored==0){
				current->bsz = current->bsz + current->link->bsz;
				current->link = current->link->link;
				//myfree(current->link);
				//vuelvo al principio
				compactado = 1;
				current = start;
			}
			//me muevo al siguiente bloque
			if(compactado == 0)
				current = current->link;
		}

}

void dealocate_item(char* clave, struct memory *start) {
	struct memory* current = start;
	struct memory* prev = NULL;
	while (current != NULL) {
		int32_t esIgual =strcmp(current->key,clave);
		if(esIgual == 0)
			break;
		else
			current = current->link;
	}
	if (current == NULL) {
		printf("No se encontro la clave\n");
	} else {
		//si el siguiente esta libre los junto en uno
		if (current->link != NULL && current->link->stored == 0) {
			current->bsz = current->bsz + current->link->bsz;
			prev = current->link;
			current->link = prev->link;
			prev->link = NULL;
			myfree(prev);

		}
		current->stored = 0;

	}
}


void dealocate_memory(int32_t criterio, struct memory *start) {
	struct memory* block = NULL;
	switch (criterio){
	case FIFO:
		block = get_fifo(start);
		break;
	case LRU:
		block = get_lru(start);
		break;
	}
	if(block == NULL)
		printf("Memoria libre, no hay items para borrar");
	else
		//lo marco como libre
		block->stored = 0;
}

struct memory* get_fifo(struct memory *start){
	struct memory *current = start;
	struct memory *fifoBlock = NULL;
	int32_t flag = 0;

	while (current != NULL) {
			if(current->stored!=0 && flag==0){
				flag = 1;
				fifoBlock = current;
			}
			if(current->stored!=0 && current->lastAccsess < fifoBlock->lastAccsess)
				fifoBlock = current;
			current = current->link;
	}
	return fifoBlock;
}

struct memory* get_lru(struct memory *start){
	struct memory *current = start;
	struct memory *lruBlock = NULL;
	int32_t flag = 0;
	while (current != NULL) {
			if(current->stored!=0 && flag==0){
				flag = 1;
				lruBlock = current;
			}
			if(current->stored!=0 && current->lastAccsess > lruBlock->lastAccsess)
				lruBlock = current;
			current = current->link;
	}
	return lruBlock;
}

struct memory* get_value(char* clave,struct memory* start,int32_t criterio){
	struct memory* current = start;
	while (current != NULL) {
		if(current->stored!=0){
			int32_t esIgual =strcmp(current->key,clave);
			if(esIgual == 0)
				break;
		}
		current = current->link;
	}
	if (current == NULL) {
		printf("No se encontro la clave\n");
		return NULL;
	} else {
		updateBLockTime(current,criterio);
		return current;
	}


}
void flush(struct memory *start){
	struct memory *current = start;
	while (current != NULL) {
		current->stored = 0;
		current = current->link;
	}
	compactar(start);
}

void show_mem_status(struct memory *start) {
	int32_t c = 1, free = 0, aloc = 0;
	struct memory* current = start;
	printf("Memory Status is::\n");
	printf("---Block---BSZ---BS---BE---Valor---Clave---Flag---keysz---datasz\n");
	while (current != NULL) {
		char* data = (char*) current->data;
		char *clave= (char*) current->key;
		printf("%d %d %p %p %s %s %d %d %d\n", c++, current->bsz, current->data, current->data+current->bsz-1, data,clave,
				current->stored, current->keysz, current->datasz);

		if (current->stored == 0) {
			free = free + current->bsz;
		} else {
			aloc = aloc + current->bsz;
		}
		current = current->link;
	}
	printf("Memoria libre= %d\n", free);
	printf("Memoria alocada= %d\n", aloc);

}


